/*
 * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
 * SPDX-License-Identifier: Apache-2.0
 */
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/idf_additions.h"
#include "esp_timer.h"
#include "esp_log.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_idf_version.h"

#include "avifile.h"
#include "avi_player.h"

static const char *TAG = "avi player"; // 日志标签

// 事件组标志位定义
#define EVENT_FPS_TIME_UP     ((1 << 0)) // 帧率定时器触发
#define EVENT_START_PLAY      ((1 << 1)) // 开始播放
#define EVENT_STOP_PLAY       ((1 << 2)) // 停止播放
#define EVENT_DEINIT          ((1 << 3)) // 反初始化
#define EVENT_DEINIT_DONE     ((1 << 4)) // 反初始化完成
#define EVENT_VIDEO_BUF_READY ((1 << 5)) // 视频帧就绪
#define EVENT_AUDIO_BUF_READY ((1 << 6)) // 音频帧就绪

#define EVENT_ALL (EVENT_FPS_TIME_UP | EVENT_START_PLAY | EVENT_STOP_PLAY | EVENT_DEINIT) // 所有控制事件

// 播放模式枚举
typedef enum {
    PLAY_FILE,   // 从文件播放
    PLAY_MEMORY, // 从内存播放
} play_mode_t;

// AVI 解析状态枚举
typedef enum {
    AVI_PARSER_NONE,   // 未初始化
    AVI_PARSER_HEADER, // 解析文件头
    AVI_PARSER_DATA,   // 解析数据帧
    AVI_PARSER_END,    // 播放结束
} avi_play_state_t;

// AVI 数据源结构体
typedef struct
{
    play_mode_t mode; // 播放模式
    union {
        struct
        {                         // 内存模式
            uint8_t *data;        // AVI 数据指针
            uint32_t size;        // 数据总大小
            uint32_t read_offset; // 当前读取位置
        } memory;
        struct
        {                   // 文件模式
            FILE *avi_file; // 文件句柄
        } file;
    };
    uint8_t *pbuffer;       // 数据缓冲区指针
    uint32_t str_size;      // 当前帧大小
    avi_play_state_t state; // 当前解析状态
    avi_typedef AVI_file;   // AVI 文件解析结构
} avi_data_t;

// AVI 播放器实例结构
typedef struct
{
    EventGroupHandle_t event_group;  // 事件组句柄
    esp_timer_handle_t timer_handle; // 帧率定时器句柄
    avi_player_config_t config;      // 用户配置
    avi_data_t avi_data;             // AVI 数据
} avi_player_t;

// 字节序反转函数 (小端转大端)
static uint32_t _REV(uint32_t value)
{
    return (value & 0x000000FFU) << 24 | (value & 0x0000FF00U) << 8 |
           (value & 0x00FF0000U) >> 8 | (value & 0xFF000000U) >> 24;
}

// 读取帧数据函数
static uint32_t read_frame(avi_data_t *avi, uint8_t *buffer, uint32_t length, uint32_t *fourcc)
{
    AVI_CHUNK_HEAD head;

    // 根据模式读取帧头
    if (avi->mode == PLAY_MEMORY) // 内存模式
    {
        // 内存模式：检查数据长度
        if (sizeof(AVI_CHUNK_HEAD) > (avi->memory.size - avi->memory.read_offset)) {
            ESP_LOGE(TAG, "帧头数据不足");
            return 0;
        }
        memcpy(&head, avi->memory.data + avi->memory.read_offset, sizeof(AVI_CHUNK_HEAD));
        avi->memory.read_offset += sizeof(AVI_CHUNK_HEAD);
    } else if (avi->mode == PLAY_FILE) // 文件模式
    {
        // 文件模式：直接读取
        fread(&head, sizeof(AVI_CHUNK_HEAD), 1, avi->file.avi_file);
    }

    if (head.FourCC) {
        /* 可在此处理 FourCC */
    }
    *fourcc = head.FourCC; // 返回帧类型标识

    // AVI规范：奇数大小需对齐
    if (head.size % 2) {
        head.size++; // 奇数字节数补1
    }

    // 读取帧数据
    if (avi->mode == PLAY_MEMORY) {
        // 内存模式：检查长度有效性
        if (head.size > (avi->memory.size - avi->memory.read_offset) || length < head.size) {
            ESP_LOGE(TAG, "frame size %" PRIu32 " cannot fit in buffer ", head.size);
            return 0;
        }
        memcpy(buffer, avi->memory.data + avi->memory.read_offset, head.size);
        avi->memory.read_offset += head.size;
    } else if (avi->mode == PLAY_FILE) {
        // 文件模式：检查缓冲区长度
        if (length < head.size) {
            ESP_LOGE(TAG, "frame size %" PRIu32 " cannot fit in buffer ", head.size);
            return 0;
        }
        fread(buffer, head.size, 1, avi->file.avi_file);
    }

    return head.size; // 返回实际读取的帧大小
}

// AVI 播放器核心状态机
static esp_err_t avi_player(avi_player_handle_t handle, size_t *BytesRD, uint32_t *Strtype)
{
    avi_player_t *player = (avi_player_t *)handle;
    uint32_t buffer_size = player->config.buffer_size;
    int ret;

    switch (player->avi_data.state) {
        case AVI_PARSER_HEADER: { // 解析文件头
            // 读取AVI文件头数据
            if (player->avi_data.mode == PLAY_MEMORY) {
                memcpy(player->avi_data.pbuffer, player->avi_data.memory.data, buffer_size);
                *BytesRD = buffer_size;
            } else {
                *BytesRD = fread(player->avi_data.pbuffer, buffer_size, 1, player->avi_data.file.avi_file);
            }

            // 解析AVI头
            ret = avi_parser(&player->avi_data.AVI_file, player->avi_data.pbuffer, *BytesRD);
            if (0 > ret) {
                ESP_LOGE(TAG, "Analysis failed (%d)", ret);
                xEventGroupSetBits(player->event_group, EVENT_STOP_PLAY);
                return ESP_FAIL;
            }

            // 设置音频时钟回调
            if (player->config.audio_set_clock_cb) {
                player->config.audio_set_clock_cb(
                    player->avi_data.AVI_file.auds_sample_rate,
                    player->avi_data.AVI_file.auds_bits,
                    player->avi_data.AVI_file.auds_channels,
                    player->config.user_data);
            }

            // 计算帧间隔时间并启动定时器
            uint32_t fps_time = 1000 * 1000 / player->avi_data.AVI_file.vids_fps;
            ESP_LOGD(TAG, "fps=%d", player->avi_data.AVI_file.vids_fps);
            esp_timer_start_periodic(player->timer_handle, fps_time);

            // 跳转到数据区开始位置
            if (player->avi_data.mode == PLAY_MEMORY) {
                player->avi_data.memory.read_offset = player->avi_data.AVI_file.movi_start;
            } else {
                fseek(player->avi_data.file.avi_file, player->avi_data.AVI_file.movi_start, SEEK_SET); //
            }

            player->avi_data.state = AVI_PARSER_DATA; // 进入数据解析状态
            *BytesRD = 0;
            // 注意：此处没有break，会继续执行DATA状态处理
        }
        case AVI_PARSER_DATA: { // 解析数据帧
            /* 清除帧就绪事件 */
            xEventGroupClearBits(player->event_group, EVENT_AUDIO_BUF_READY | EVENT_VIDEO_BUF_READY);
            while (1) {
                // 读取一帧数据
                player->avi_data.str_size = read_frame(&player->avi_data, player->avi_data.pbuffer, buffer_size, Strtype);
                ESP_LOGD(TAG, "frame type=%" PRIu32 ", size=%" PRIu32 "", *Strtype, player->avi_data.str_size);
                *BytesRD += player->avi_data.str_size + 8; // +8 包含帧头大小

                // 检查是否播放结束
                if (*BytesRD >= player->avi_data.AVI_file.movi_size) {
                    ESP_LOGI(TAG, "play end");
                    player->avi_data.state = AVI_PARSER_END;
                    xEventGroupSetBits(player->event_group, EVENT_STOP_PLAY);
                    return ESP_OK;
                }

                // 视频帧处理 (DC_ID = '00dc')
                if ((*Strtype & 0xFFFF0000) == DC_ID) {
                    int64_t fr_end = esp_timer_get_time(); // 记录起始时间
                    if (player->config.video_cb) {
                        // 构造视频帧数据
                        frame_data_t data = {
                            .data = player->avi_data.pbuffer,
                            .data_bytes = player->avi_data.str_size,
                            .type = FRAME_TYPE_VIDEO,
                            .video_info.width = player->avi_data.AVI_file.vids_width,
                            .video_info.height = player->avi_data.AVI_file.vids_height,
                            .video_info.frame_format = player->avi_data.AVI_file.vids_format,
                        };
                        // 调用视频回调
                        player->config.video_cb(&data, player->config.user_data);
                    }
                    xEventGroupSetBits(player->event_group, EVENT_VIDEO_BUF_READY); // 标记视频帧就绪
                    ESP_LOGD(TAG, "绘制耗时 %" PRIu32 "ms", (uint32_t)((esp_timer_get_time() - fr_end) / 1000));
                    break; // 退出循环等待下一帧
                }
                // 音频帧处理 (WB_ID = '00wb')
                else if ((*Strtype & 0xFFFF0000) == WB_ID) {
                    if (player->config.audio_cb) {
                        // 构造音频帧数据
                        frame_data_t data = {
                            .data = player->avi_data.pbuffer,
                            .data_bytes = player->avi_data.str_size,
                            .type = FRAME_TYPE_AUDIO,
                            .audio_info.channel = player->avi_data.AVI_file.auds_channels,
                            .audio_info.bits_per_sample = player->avi_data.AVI_file.auds_bits,
                            .audio_info.sample_rate = player->avi_data.AVI_file.auds_sample_rate,
                            .audio_info.format = FORMAT_PCM,
                        };
                        // 调用音频回调
                        player->config.audio_cb(&data, player->config.user_data);
                    }
                    xEventGroupSetBits(player->event_group, EVENT_AUDIO_BUF_READY); // 标记音频帧就绪
                }
                // 未知帧类型
                else {
                    ESP_LOGE(TAG, "unknown frame type %" PRIx32 "", *Strtype);
                    xEventGroupSetBits(player->event_group, EVENT_STOP_PLAY);
                    return ESP_FAIL;
                }
            }
            break;
        }
        case AVI_PARSER_END:                      // 播放结束处理
            esp_timer_stop(player->timer_handle); // 停止定时器
            // 关闭文件（如果是文件模式）
            if (player->avi_data.mode == PLAY_FILE) {
                fclose(player->avi_data.file.avi_file);
            }

            player->avi_data.state = AVI_PARSER_NONE; // 重置状态
            // 调用播放结束回调
            if (player->config.avi_play_end_cb) {
                player->config.avi_play_end_cb(player->config.user_data);
            }
            break;
        default:
            break;
    }
    return ESP_OK;
}

// AVI 播放器任务主函数
static void avi_player_task(void *args)
{
    avi_player_t *player = (avi_player_t *)args;
    EventBits_t uxBits;
    bool exit = false;
    size_t BytesRD = 0;
    uint32_t Strtype = 0;
    while (!exit) {
        // 等待事件触发
        uxBits = xEventGroupWaitBits(player->event_group, EVENT_ALL, pdTRUE, pdFALSE, portMAX_DELAY);

        // 处理停止播放事件
        if (uxBits & EVENT_STOP_PLAY) {
            player->avi_data.state = AVI_PARSER_END;
            esp_err_t ret = avi_player(player, &BytesRD, &Strtype);
            if (ret != ESP_OK) {
                ESP_LOGE(TAG, "AVI Analysis failed");
            }
        }

        // 处理开始播放事件
        if (uxBits & EVENT_START_PLAY) {
            player->avi_data.state = AVI_PARSER_HEADER;
            esp_err_t ret = avi_player(player, &BytesRD, &Strtype);
            if (ret != ESP_OK) {
                ESP_LOGE(TAG, "AVI Analysis failed");
            }
        }

        // 处理帧定时器事件
        if (uxBits & EVENT_FPS_TIME_UP) {
            esp_err_t ret = avi_player(player, &BytesRD, &Strtype);
            if (ret != ESP_OK) {
                ESP_LOGE(TAG, "AVI Analysis failed");
            }
        }

        // 处理反初始化事件
        if (uxBits & EVENT_DEINIT) {
            exit = true;
            break;
        }
    }
    // 标记反初始化完成
    xEventGroupSetBits(player->event_group, EVENT_DEINIT_DONE);
    // 安全删除任务
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
    vTaskDeleteWithCaps(NULL);
#else
    vTaskDelete(NULL);
#endif
}

// 获取视频帧数据 API
esp_err_t avi_player_get_video_buffer(avi_player_handle_t handle, void **buffer, size_t *buffer_size, video_frame_info_t *info, TickType_t ticks_to_wait)
{
    avi_player_t *player = (avi_player_t *)handle;
    // 参数检查
    ESP_RETURN_ON_FALSE(buffer != NULL, ESP_ERR_INVALID_ARG, TAG, "缓冲区不能为NULL");
    ESP_RETURN_ON_FALSE(info != NULL, ESP_ERR_INVALID_ARG, TAG, "视频信息不能为NULL");
    ESP_RETURN_ON_FALSE(buffer_size != NULL, ESP_ERR_INVALID_ARG, TAG, "缓冲区大小不能为0");

    // 等待视频帧就绪事件
    EventBits_t uxBits = xEventGroupWaitBits(player->event_group, EVENT_VIDEO_BUF_READY, pdTRUE, pdFALSE, ticks_to_wait);
    if (!(uxBits & EVENT_VIDEO_BUF_READY)) {
        return ESP_ERR_TIMEOUT;
    }

    // 检查缓冲区大小
    if (*buffer_size < player->avi_data.str_size) {
        ESP_LOGE(TAG, "缓冲区大小不足");
        return ESP_ERR_NO_MEM;
    }

    // 复制数据并填充信息
    memcpy(*buffer, player->avi_data.pbuffer, player->avi_data.str_size);
    *buffer_size = player->avi_data.str_size;
    info->width = player->avi_data.AVI_file.vids_width;
    info->height = player->avi_data.AVI_file.vids_height;
    info->frame_format = player->avi_data.AVI_file.vids_format;
    return ESP_OK;
}

// 获取音频帧数据 API (实现逻辑与视频类似)
esp_err_t avi_player_get_audio_buffer(avi_player_handle_t handle, void **buffer, size_t *buffer_size, audio_frame_info_t *info, TickType_t ticks_to_wait)
{
    /* 类似视频帧处理，省略详细注释 */
    // ... [代码实现] ...
    return ESP_OK;
}

// 从内存启动播放
esp_err_t avi_player_play_from_memory(avi_player_handle_t handle, uint8_t *avi_data, size_t avi_size)
{
    avi_player_t *player = (avi_player_t *)handle;
    // 状态检查
    ESP_RETURN_ON_FALSE(player->avi_data.state == AVI_PARSER_NONE, ESP_ERR_INVALID_STATE, TAG, "播放器未就绪");

    // 设置内存播放参数
    player->avi_data.mode = PLAY_MEMORY;
    player->avi_data.memory.data = avi_data;
    player->avi_data.memory.size = avi_size;
    player->avi_data.memory.read_offset = 0;

    // 发送开始播放事件
    xEventGroupSetBits(player->event_group, EVENT_START_PLAY);
    return ESP_OK;
}

// 从文件启动播放
esp_err_t avi_player_play_from_file(avi_player_handle_t handle, const char *filename)
{
    avi_player_t *player = (avi_player_t *)handle;
    // 如果不是 AVI_PARSER_NONE 状态，说明播放器正在播放，返回错误
    ESP_RETURN_ON_FALSE(player->avi_data.state == AVI_PARSER_NONE, ESP_ERR_INVALID_STATE, TAG, "AVI player not ready");

    // 打开文件
    player->avi_data.mode = PLAY_FILE;
    player->avi_data.file.avi_file = fopen(filename, "rb");
    if (player->avi_data.file.avi_file == NULL) {
        ESP_LOGE(TAG, "cannot open file %s", filename);
        return ESP_FAIL;
    }

    // 发送开始播放事件
    xEventGroupSetBits(player->event_group, EVENT_START_PLAY);
    return ESP_OK;
}

/*
 *@brief 停止播放
*/
// 停止播放
esp_err_t avi_player_play_stop(avi_player_handle_t handle)
{
    avi_player_t *player = (avi_player_t *)handle;
    // 检查是否在播放中
    ESP_RETURN_ON_FALSE(player->avi_data.state == AVI_PARSER_HEADER || player->avi_data.state == AVI_PARSER_DATA,
                        ESP_ERR_INVALID_STATE, TAG, "AVI player not playing");
    // 发送停止事件
    xEventGroupSetBits(player->event_group, EVENT_STOP_PLAY);
    return ESP_OK;
}

// 帧率定时器回调函数
static void esp_timer_cb(void *arg)
{
    avi_player_t *player = (avi_player_t *)arg;
    // 触发帧处理事件
    xEventGroupSetBits(player->event_group, EVENT_FPS_TIME_UP);
}

// 播放器初始化
esp_err_t avi_player_init(avi_player_config_t config, avi_player_handle_t *handle)
{
    //ESP_LOGI(TAG, "AVI 播放器版本: %d.%d.%d", AVI_PLAYER_VER_MAJOR, AVI_PLAYER_VER_MINOR, AVI_PLAYER_VER_PATCH);

    // 创建播放器实例
    avi_player_t *player = (avi_player_t *)calloc(1, sizeof(avi_player_t));
    player->config = config;

    // 设置默认参数
    if (player->config.buffer_size == 0) {
        player->config.buffer_size = 20 * 1024; // 默认20KB缓冲区
    }
    if (player->config.priority == 0) {
        player->config.priority = 5; // 默认任务优先级5
    }
    if (player->config.stack_size == 0) {
        player->config.stack_size = 4096; // 默认4KB任务栈
    }

    // 分配帧缓冲区
    player->avi_data.pbuffer = heap_caps_malloc(player->config.buffer_size, MALLOC_CAP_SPIRAM|MALLOC_CAP_8BIT);
    ESP_RETURN_ON_FALSE(player->avi_data.pbuffer != NULL, ESP_ERR_NO_MEM, TAG, "Cannot alloc memory for player");

    // 创建帧率定时器
    esp_timer_create_args_t timer = { 0 };
    timer.arg = player;
    timer.callback = esp_timer_cb;
    timer.dispatch_method = ESP_TIMER_TASK;
    timer.name = "avi_player_timer";
    esp_timer_create(&timer, &player->timer_handle);

    // 创建事件组
    player->event_group = xEventGroupCreate();
    assert(player->event_group);
    ESP_RETURN_ON_FALSE(player->event_group != NULL, ESP_ERR_NO_MEM, TAG, "Cannot create event group");

    *handle = (avi_player_handle_t *)player;

    // 创建播放器任务（兼容不同IDF版本）
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
    int stack_caps = config.stack_in_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL;
    xTaskCreatePinnedToCoreWithCaps(avi_player_task, "avi_player", player->config.stack_size, player, player->config.priority, NULL, player->config.coreID, stack_caps);
#else
    xTaskCreatePinnedToCore(avi_player_task, "avi_player", player->config.stack_size, player, player->config.priority, NULL, player->config.coreID);
#endif
    return ESP_OK;
}

// 播放器反初始化
esp_err_t avi_player_deinit(avi_player_handle_t handle)
{
    avi_player_t *player = (avi_player_t *)handle;
    if (player == NULL) {
        return ESP_FAIL;
    }

    // 发送反初始化事件
    xEventGroupSetBits(player->event_group, EVENT_DEINIT);
    // 等待反初始化完成
    EventBits_t uxBits = xEventGroupWaitBits(player->event_group, EVENT_DEINIT_DONE, pdTRUE, pdTRUE, pdMS_TO_TICKS(1000));
    if (!(uxBits & EVENT_DEINIT_DONE)) {
        ESP_LOGE(TAG, "反初始化超时");
        return ESP_ERR_TIMEOUT;
    }

    // 释放资源
    if (player->timer_handle != NULL) {
        esp_timer_stop(player->timer_handle);
        esp_timer_delete(player->timer_handle);
    }

    if (player->avi_data.pbuffer != NULL) {
        heap_caps_free(player->avi_data.pbuffer);
    }

    if (player->event_group != NULL) {
        vEventGroupDelete(player->event_group);
    }

    free(player);
    return ESP_OK;
}